package com.open.basic.util;

import com.open.basic.exception.SystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * @author: system
 * @Date: 2024/4/19 15:00
 * @Version: V1.0
 * @description: desc:数据工具类
 */
public final class DateUtils extends org.apache.commons.lang3.time.DateUtils {

    private static final Logger LOG = LoggerFactory.getLogger(DateUtils.class);

    public static final String ORACLE_DATETIME_FORMAT = "yyyy-MM-dd HH24:mm:ss";
    /**
     * mysql的日期格式 2012-05-01 23:59:59
     * %Y：代表4位的年份
     * <p>
     * %y：代表2为的年份
     * <p>
     * %m：代表月, 格式为(01……12)
     * <p>
     * %c：代表月, 格式为(1……12)
     * <p>
     * %d：代表月份中的天数,格式为(00……31)
     * <p>
     * %e：代表月份中的天数, 格式为(0……31)
     * <p>
     * %H：代表小时,格式为(00……23)
     * <p>
     * %k：代表 小时,格式为(0……23)
     * <p>
     * %h： 代表小时,格式为(01……12)
     * <p>
     * %I： 代表小时,格式为(01……12)
     * <p>
     * %l ：代表小时,格式为(1……12)
     * <p>
     * %i： 代表分钟, 格式为(00……59)
     * <p>
     * %r：代表 时间,格式为12 小时(hh:mm:ss [AP]M)
     * <p>
     * %T：代表 时间,格式为24 小时(hh:mm:ss)
     * <p>
     * %S：代表 秒,格式为(00……59)
     * <p>
     * %s：代表 秒,格式为(00……59)
     */
    public static final String MYSQL_DATETIME_FORMAT = "%Y-%m-%d %T";

    public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    public static final String DATETIME_FORMAT_COMPACT = "yyyyMMdd HH:mm:ss";

    public static final String DATE_FORMAT = "yyyy-MM-dd";
    public static final String DATE_FORMAT_SLASH = "yyyy/MM/dd";

    public static final String yyyyMMdd = "yyyyMMdd";

    public static final String yyyyMM = "yyyyMM";

    public static final String yyyyMMddHHmm = "yyyyMMddHHmm";

    /**
     * yyyyMMddHHmmss
     */
    public static final String yyyyMMddHHmmss = "yyyyMMddHHmmss";

    public static final String yyyyMMddHHmmssSSS = "yyyyMMddHHmmssSSS";


    private DateUtils() {
        // 禁止实例化
    }

    /**
     * 获取当前时间字符串 格式：yyyyMMddHHmmss
     *
     * @return
     */
    public static String getDatetimeString() {
        return getDateTimeString(null);
    }

    /**
     * 获取指定时间字符串 格式：yyyyMMddHHmmss
     *
     * @param localDateTime 时间
     * @return
     */
    public static String getDateTimeString(LocalDateTime localDateTime) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(yyyyMMddHHmmss);
        return localDateTime == null ? LocalDateTime.now().format(dateTimeFormatter) : localDateTime.format(dateTimeFormatter);
    }

    /**
     * 获取当前时间戳的毫秒数，自1970年
     *
     * @return 整型毫秒数
     */
    public static long time() {
        Timestamp time = now();
        return time.getTime();
    }

    /**
     * 返回当前时间格式化后字符串 格式：yyyy-MM-dd HH:mm:ss
     *
     * @return 当前时间的字符串
     */
    public static String getCurrentTime() {
        return format(now(), DATETIME_FORMAT);
    }

    /**
     * 获取系统时间
     *
     * @return sql 时间戳
     */
    public static Timestamp now() {
        return new Timestamp(System.currentTimeMillis());
    }

    /**
     * 根据指定的格式输入时间字符串
     *
     * @param format 日期格式
     * @return 当前时间字符串
     * @throws SystemException 异常
     */
    public static String now(String format) {
        if (!StringUtils.hasText(format)) {
            throw new SystemException("请指定日期格式");
        }
        DateFormat fmt = new SimpleDateFormat(format);
        Date date = new Date();
        return fmt.format(date);
    }

    /**
     * 获取时间字符串
     *
     * @param time   sql 时间戳
     * @param format 日期格式
     * @return 格式化后字符串
     * @throws SystemException 异常
     */
    public static String format(Timestamp time, String format) {
        Assert.notNull(time, "null string");
        Assert.notNull(format, "null format");
        DateFormat fmt = new SimpleDateFormat(format);
        return fmt.format(time);
    }

    /**
     * 获取指定时间的格式化串
     *
     * @param date   日期对象
     * @param format 日期格式
     * @return 格式化字符串
     * @throws SystemException 异常
     */
    public static String format(Date date, String format) {
        Assert.notNull(date, "null string");
        Assert.notNull(format, "null format");
        SimpleDateFormat fmt = new SimpleDateFormat(format);
        return date != null ? fmt.format(date) : "";
    }

    /**
     * 判断时间是否符合格式要求
     *
     * @param str    日期字符串
     * @param format 日期格式
     * @return 是否是日期
     * @throws SystemException 异常
     */
    public static boolean isValid(String str, String format) {
        Assert.notNull(str, "null string");
        Assert.notNull(format, "null format");
        boolean flag = false;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            sdf.parse(str);
            flag = true;
        } catch (ParseException e) {
            LOG.error(e.getMessage(), e);
            flag = false;
        }
        return flag;
    }


    /**
     * 按指定格式将字符串转换为日期对象
     *
     * @param str    时间字符串
     * @param format 时间格式
     * @return 日期对象
     * @throws SystemException 异常
     */
    public static Date parse(String str, String format) {
        Assert.notNull(str, "null date string");
        Assert.notNull(format, "null date format");
        DateFormat df = new SimpleDateFormat(format);
        try {
            return df.parse(str);
        } catch (ParseException e) {
            throw new SystemException("系统转换日期字符串时出错！", e);
        }
    }


    /**
     * @param time yyyy-MM-dd HH:mm:ss格式的时间
     * @return sql 时间戳
     */
    public static Timestamp toTimestamp(String time) {
        DateFormat format = new SimpleDateFormat(DateUtils.DATETIME_FORMAT);
        format.setLenient(false);
        Timestamp ts = null;
        try {
            ts = new Timestamp(format.parse(time).getTime());
        } catch (ParseException e) {
            throw new SystemException("", e);
        }
        return ts;
    }


    /**
     * 将指定格式的日期字符串转成Timestamp
     *
     * @param time   时间字符串
     * @param format 日期格式
     * @return 返回 sql 时间戳
     * @throws SystemException 异常
     */
    public static Timestamp toTimestamp(String time, String format) {
        if (!StringUtils.hasText(time)) {
            throw new SystemException("请指定字符串时间");
        }
        if (!StringUtils.hasText(format)) {
            throw new SystemException("请指定日期格式");
        }
        DateFormat fmt = new SimpleDateFormat(format);
        fmt.setLenient(false);
        Timestamp ts = null;
        try {
            ts = new Timestamp(fmt.parse(time).getTime());
        } catch (ParseException e) {
            throw new SystemException("", e);
        }
        return ts;
    }

    /**
     * 转换为TimeStamp
     *
     * @param time 毫秒数
     * @return SQL TimeStamp
     */
    public static Timestamp toTimestamp(long time) {
        return new Timestamp(time);
    }

    /**
     * 转换为TimeStamp
     *
     * @param date 日期对象
     * @return SQL TimeStamp
     */
    public static Timestamp toTimestamp(Date date) {
        return new Timestamp(date.getTime());
    }

    /**
     * LocalDate 转换为Date
     *
     * @param localDate 带区域属性的本地日期
     * @return Date
     */
    public static Date asDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * LocalDateTime 转换为Date
     *
     * @param localDateTime 本地时间
     * @return Date
     */
    public static Date asDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * Date转换为LocalDate
     *
     * @param date 时间
     * @return 本地时间
     */
    public static LocalDate asLocalDate(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * LocalDateTime
     *
     * @param date 时间
     * @return 本地时间
     */
    public static LocalDateTime asLocalDateTime(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * 获取两个日期内的所有日期
     *
     * @param start 开始时间
     * @param end   结束时间
     * @return 相差的日期
     */
    public static List<LocalDate> getBetweenDates(LocalDate start, LocalDate end) {
        AssertUtil.notNull(start, "start is null");
        AssertUtil.notNull(end, "end is null");
        AssertUtil.assertTrue(start.isBefore(end) || start.isEqual(end), "开始时间不能大于结束时间");
        List<LocalDate> result = new ArrayList<>();
        // 获取两个时间相差天数
        long num = ChronoUnit.DAYS.between(start, end);
        for (int i = 0; i <= num; i++) {
            result.add(start.plusDays(i));
        }
        return result;
    }
}
